package org.excel;

import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class WPSExcelImportImgExtractor {

    /**
     * 提取 Excel 中的图片，并返回单元格与图片路径的映射
     * <p>
     * WPS 的内置函数无法提取图片，需要使用第三方库解析XML文件。
     * WPS 内嵌图片会把图片做DISPIMG函数处理，如=DISPIMG("ID_9D6E8C240C8945178DFF238232B217BF",1)
     * 我们可以将.xlsx 文件后缀改成.zip后解压，即可看到
     * 在xl路径下的cellimages.xml文件中，可以看到函数中的id值
     * 并且在cellimages.xml.rels中可以看到函数与图片之间的关系，而图片就位于xl/media路径下
     *
     * @param inputStream Excel 文件的输入流
     * @param outputDir   图片保存的目标目录
     * @return 单元格位置（如 A1）与图片路径的映射
     * @throws Exception 异常
     */
    public Map<String, String> extractImages(InputStream inputStream, String outputDir) throws Exception {
        Map<String, String> cellImageMap = new HashMap<>();
        Map<String, String> relsMap = new HashMap<>();
        Map<String, byte[]> imagesData = new HashMap<>();
        // 创建目标目录
        File dir = new File(outputDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        ZipInputStream zis = new ZipInputStream(inputStream);
        ZipEntry zipEntry;
        ByteArrayOutputStream baos = null;
        String sheetXml = null;
        String cellImagesXml = null;
        String cellImagesRelsXml = null;

        // 首先遍历所有的Zip条目，找到需要的XML和图片文件
        while ((zipEntry = zis.getNextEntry()) != null) {
            String entryName = zipEntry.getName();
            if ("xl/cellimages.xml".equals(entryName)) {
                baos = new ByteArrayOutputStream();
                IOUtils.copy(zis, baos);
                cellImagesXml = baos.toString("UTF-8");
                baos.close();
            } else if ("xl/_rels/cellimages.xml.rels".equals(entryName)) {
                baos = new ByteArrayOutputStream();
                IOUtils.copy(zis, baos);
                cellImagesRelsXml = baos.toString("UTF-8");
                baos.close();
            } else if (entryName.startsWith("xl/media/")) {
                byte[] imageBytes = IOUtils.toByteArray(zis);
                String imageName = entryName.substring("xl/media/".length());
                imagesData.put(imageName, imageBytes);
            }
            zis.closeEntry();
        }
        zis.close();

        // 解析cellimages.xml.rels，建立rId到图片文件名的映射
        if (cellImagesRelsXml != null) {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document relsDoc = builder.parse(new ByteArrayInputStream(cellImagesRelsXml.getBytes("UTF-8")));
            NodeList relNodes = relsDoc.getElementsByTagName("Relationship");
            for (int i = 0; i < relNodes.getLength(); i++) {
                Element relElement = (Element) relNodes.item(i);
                String rId = relElement.getAttribute("Id");
                // e.g., "media/image1.png"
                String target = relElement.getAttribute("Target");
                // 接受本地文件格式
                if (target.startsWith("media/")) {
                    relsMap.put(rId, target.substring("media/".length()));
                }
                // 接受https格式
                if (target.startsWith("https://")) {
                    relsMap.put(rId, target);
                }
            }
        }

        // 解析cellimages.xml，提取图片与单元格的关系
        if (cellImagesXml != null) {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document cellImagesDoc = builder.parse(new ByteArrayInputStream(cellImagesXml.getBytes("UTF-8")));
            NodeList cellImageNodes = cellImagesDoc.getElementsByTagName("etc:cellImage");
            for (int i = 0; i < cellImageNodes.getLength(); i++) {
                Element cellImageElement = (Element) cellImageNodes.item(i);
                // 获取图片 name 属性，如 "ID_6C483737A6AC427DAA4E4974252FB8A8"
                Element picElement = (Element) cellImageElement.getElementsByTagName("xdr:pic").item(0);
                Element cNvPr = (Element) picElement.getElementsByTagName("xdr:cNvPr").item(0);
                // e.g., "ID_6C483737A6AC427DAA4E4974252FB8A8"
                String imageName = cNvPr.getAttribute("name");
                // 获取 r:embed 属性，如 "rId1"
                Element blipFill = (Element) picElement.getElementsByTagName("xdr:blipFill").item(0);
                Element blip = (Element) blipFill.getElementsByTagName("a:blip").item(0);
                // e.g., "rId1"
                String rId = blip.getAttribute("r:embed");
                String linkRid = blip.getAttribute("r:link");
                // e.g., "image1.png"
                String imageFileName;

                // TODO: 根据需要确定图片对应的单元格位置
                // 由于cellimages.xml中没有直接包含单元格位置的信息，这里需要通过其他途径获取
                // 例如，可以通过图片的位置信息（如x, y坐标）与单元格的位置对应
                // 但是这需要解析更多的XML信息，这里假设图片对应的顺序与数据行对应

                // 保存图片到本地
                byte[] imageBytes = null;
                if (linkRid != null && linkRid.trim().length() > 0) {
                    imageFileName = relsMap.get(linkRid);
                    imageBytes = downloadImage(imageFileName);
                } else {
                    imageFileName = relsMap.get(rId);
                    imageBytes = imagesData.get(imageFileName);
                }
                if (imageBytes != null) {
                    String savedImagePath = outputDir + File.separator;
                    if (linkRid != null && linkRid.trim().length() > 0) {
                        savedImagePath=savedImagePath + getFileNameFromUrl(imageFileName);
                    }else{
                        savedImagePath=savedImagePath + imageFileName;
                    }
                    System.out.println("===========获取图片进入保存=============="+savedImagePath);
                    FileOutputStream fos = new FileOutputStream(savedImagePath);
                    fos.write(imageBytes);
                    fos.close();
                    // 由于缺少单元格位置信息，这里需要自定义逻辑进行映射
                    // 例如，可以将图片顺序与数据行顺序对应
                    // 这里将图片名存储到一个列表中，后续与数据行进行关联
                    cellImageMap.put(imageName, savedImagePath);
                }
            }
        }

        return cellImageMap;
    }

    private String getFileNameFromUrl(String url) {
        try {
            URI uri = new URI(url);
            String path = uri.getPath();

            // 处理空路径情况
            if (path == null || path.isEmpty()) {
                return "unknown_" + UUID.randomUUID().toString().substring(0, 8) + ".jpg";
            }

            // 获取文件名（最后一个非空路径段）
            String[] pathSegments = path.split("/");
            String rawName = "";
            for (int i = pathSegments.length - 1; i >= 0; i--) {
                if (!pathSegments[i].isEmpty()) {
                    rawName = pathSegments[i];
                    break;
                }
            }

            // 移除查询参数（如 ?width=100）
            int queryIndex = rawName.indexOf('?');
            if (queryIndex != -1) {
                rawName = rawName.substring(0, queryIndex);
            }

            // 替换非法文件名字符
            rawName = rawName.replaceAll("[\\\\/:*?\"<>|]", "_");

            // 处理文件扩展名
            int lastDotIndex = rawName.lastIndexOf('.');
            if (lastDotIndex == -1) {
                // 没有扩展名 → 添加默认 .jpg
                rawName += ".jpg";
            } else if (lastDotIndex == rawName.length() - 1) {
                // 以点结尾 → 替换为 .jpg（如 "file." → "file.jpg"）
                rawName = rawName.substring(0, lastDotIndex) + ".jpg";
            } else {
                // 有扩展名 → 保留（如 "image.png" → "image.png"）
                String ext = rawName.substring(lastDotIndex + 1);
                if (ext.isEmpty()) {
                    rawName = rawName.substring(0, lastDotIndex) + ".jpg";
                }
            }

            return rawName;
        } catch (URISyntaxException e) {
            // 非法URL格式 → 生成随机文件名
            return "invalid_" + UUID.randomUUID().toString().substring(0, 8) + ".jpg";
        }
    }


    private byte[] downloadImage(String urlString) throws IOException {
        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(10000);

        try (InputStream is = conn.getInputStream();
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }
            return baos.toByteArray();
        } finally {
            conn.disconnect();
        }
    }

}
